#ifndef STRING_UTIL_H
#define STRING_UTIL_H
#include <Config.h>
#include <BasicTypes.h>
#include "string.h"
#ifdef WIN32
#include <winsock2.h>
#include <windows.h>
#include <stdlib.h>
#else
#include <ctype.h>
#endif
#include <string>
#include <sstream>
using namespace std;


inline const char* trimleft(const char* str)
{
	while(*str > 0 && isspace(*str))
	{
		++str;
	}
	return str;
}
inline char* trimleft(char* str)
{
	while(*str > 0 && isspace(*str))
	{
		++str;
	}
	return str;
}
inline char* trimright(char* str)
{
	int i = (int)strlen(str) - 1;
	while(i >= 0)
	{
		if(str[i] > 0 && isspace(str[i]))
			--i;
		else
			break;
	}
	str[i+1] = '\0';
	return str;
}
inline char* trim(char* str)
{
	str = trimleft(str);
	return trimright(str);
}
inline string& trimleft(string& str)
{
	if(str.empty())
		return str;
	size_t i = 0;
	for(; str[i] > 0 && isspace(str[i]); ++i);
	str = &str[i];
	return str;
}
inline string& trimright(string& str)
{
	if(str.empty())
		return str;
	int i = (int)str.length() - 1;
	while(i >= 0)
	{
		if(str[i] > 0 && isspace(str[i]))
			--i;
		else
			break;
	}
	str.resize(i + 1);
	return str;
}
inline string& trim(string& str)
{
	if(str.empty())
		return str;
	int i = (int)str.length() - 1;
	while(i >= 0)
	{
		if(str[i] > 0 && isspace(str[i]))
			--i;
		else
			break;
	}
	str[i+1] = '\0';
	for(i = 0; str[i] > 0 && isspace(str[i]); ++i);
	str = &str[i];
	return str;
}

inline string trim(const string& str)
{
	string temp = str;
	return trim(temp);
}

inline bool start_with(const string& str, const string& prefix)
{
	if(str.length() < prefix.length())
		return false;
	return str.substr(0, prefix.length()) == prefix;
}
inline bool end_with(const string& str, const string& postfix)
{
	if(str.length() < postfix.length())
		return false;
	return str.substr(str.length() - postfix.length()) == postfix;
}

inline string& tolower(string& str)
{
	for(size_t i = 0; i < str.length(); ++i)
	{
		char c = str[i];
		if(c >= 'A' && c <= 'Z')
			str[i] = c + ('a' - 'A');
	}
	return str;
}
inline string tolower(const string& str)
{
	string temp = str;
	return tolower(temp);
}
inline string& toupper(string& str)
{
	for(size_t i = 0; i < str.length(); ++i)
	{
		char c = str[i];
		if(c >= 'a' && c <= 'z')
			str[i] = c - ('a' - 'A');
	}
	return str;
}
inline string toupper(const string& str)
{
	string temp = str;
	return toupper(temp);
}
#ifdef WIN32
inline int strcasecmp(const char* str1, const char* str2)
{
	return stricmp(str1, str2);
}
#endif
template<typename T>
string ToString(const T& num)
{
	ostringstream ostr;
	ostr << num;
	return ostr.str();
}
template<typename T>
bool ParseString(const string& str, T& num)
{
	istringstream istr(str);
	istr >> num;
	return !istr.fail();
}

template<class _Elem, class _Traits, class _Alloc, class _Container>
inline void split_string(
		const basic_string<_Elem, _Traits, _Alloc>& text, 
		const basic_string<_Elem, _Traits, _Alloc>& seperator, 
		_Container& results)
{
	if(text.size() < seperator.size() || seperator.size() == 0)
	{
		results.push_back(text);
		return;
	}
	typename basic_string<_Elem, _Traits, _Alloc>::size_type chars_checked = 0;
	typename basic_string<_Elem, _Traits, _Alloc>::size_type match_count = 0;
	typename basic_string<_Elem, _Traits, _Alloc>::size_type start_index = 0;
	typename basic_string<_Elem, _Traits, _Alloc>::size_type text_minus_seperator = text.size() - seperator.size();
	_Elem cur_char = 0;
	for(; text_minus_seperator >= chars_checked - match_count; ++chars_checked)
	{
		cur_char = text[chars_checked];
		if(cur_char == seperator[match_count])
		{
			++match_count;
			if(match_count == seperator.size())
			{
				typename basic_string<_Elem, _Traits, _Alloc>::size_type string_len = chars_checked + 1 - start_index - seperator.size();
				if(string_len > 0)
				{
					results.push_back(text.substr(start_index, string_len));
				}
				start_index += string_len + seperator.size();
				match_count = 0;
			}
		}
		else
		{
			match_count = 0;
		}
	}
	if(start_index < text.size())
	{
		results.push_back(text.substr(start_index));
	}
	return;
}


template<class _Elem, class _Traits, class _Alloc>
inline basic_string<_Elem, _Traits, _Alloc> replace_string(
	const basic_string<_Elem, _Traits, _Alloc>& text, 
	const basic_string<_Elem, _Traits, _Alloc>& src,
	const basic_string<_Elem, _Traits, _Alloc>& dest)
{
	if(text.size() < src.size() || src.size() == 0)
		return text;
	basic_ostringstream<_Elem, _Traits, _Alloc> ostr;
	typename basic_string<_Elem, _Traits, _Alloc>::size_type match_count = 0;
	typename basic_string<_Elem, _Traits, _Alloc>::size_type chars_checked = 0;
	typename basic_string<_Elem, _Traits, _Alloc>::size_type text_minus_src = text.size() - src.size();
	_Elem cur_char = 0;
	for(; text_minus_src >= chars_checked - match_count; ++chars_checked)
	{
		cur_char = text[chars_checked];
		if(cur_char == src[match_count])
		{
			++match_count;
			if(match_count == src.size())
			{
				ostr << dest;
				match_count = 0;
			}
		}
		else
		{
			if(match_count > 0)
			{
				ostr << src.substr(0, match_count);
				match_count = 0;
			}
			ostr << cur_char;
		}
	}
	if(chars_checked < text.size())
	{
		ostr << text.substr(chars_checked - match_count);
	}
	return ostr.str();
}


template<class _Elem,class _Traits,class _Alloc> 
inline	basic_istream<_Elem, _Traits>& getline
(basic_istream<_Elem, _Traits>& istr,	basic_string<_Elem, _Traits, _Alloc>& line,	const basic_string<_Elem, _Traits, _Alloc>& postfix)
{
	typedef basic_istream<_Elem, _Traits> _Myis;
	typedef basic_string<_Elem, _Traits, _Alloc> _Mystr;

	ios_base::iostate _State = ios_base::goodbit;
	bool _Changed = false;
	const typename _Myis::sentry _Ok(istr, true);
	
	if(_Ok)
	{
		try
		{
			line.erase();
			string::size_type match_count = 0;
			typename _Traits::int_type _Metadelim = _Traits::to_int_type(postfix[match_count]);
			typename _Traits::int_type _Meta = istr.rdbuf()->sgetc();

			for (; ; _Meta = istr.rdbuf()->snextc())
			{
				if (_Traits::eq_int_type(_Traits::eof(), _Meta))
				{	// end of file, quit
					_State |= ios_base::eofbit;
					break;
				}
				else if (_Traits::eq_int_type(_Meta, _Metadelim))
				{
					if(++match_count == postfix.length())
					{
						// got all delimiter, discard it and quit
						_Changed = true;
						istr.rdbuf()->sbumpc();
						break;
					}
					_Metadelim = _Traits::to_int_type(postfix[match_count]);
				}
				else
				{	// got a character
					if(match_count > 0)
					{
						line += postfix.substr(0, match_count);
						match_count = 0;
						_Metadelim = _Traits::to_int_type(postfix[match_count]);
					}
					// add it to string
					line += _Traits::to_char_type(_Meta);
					_Changed = true;
				}
			}
		}
        catch(...)
		{
			istr.setstate(ios_base::badbit);
		}
	}

	if (!_Changed)
		_State |= ios_base::failbit;
	istr.setstate(_State);
	return (istr);
}

template<class _Elem,class _Traits,class _Alloc> 
inline	basic_istream<_Elem, _Traits>& getline_resume
(basic_istream<_Elem, _Traits>& istr,	basic_string<_Elem, _Traits, _Alloc>& line,	typename basic_string<_Elem, _Traits, _Alloc>::size_type& match_count, const basic_string<_Elem, _Traits, _Alloc>& postfix)
{
	assert(match_count >= 0 && match_count < postfix.size());
	typedef basic_istream<_Elem, _Traits> _Myis;
	typedef basic_string<_Elem, _Traits, _Alloc> _Mystr;

	ios_base::iostate _State = ios_base::goodbit;
	bool _Changed = false;
	const typename _Myis::sentry _Ok(istr, true);
	
	if(_Ok)
	{
		try
		{
			typename _Traits::int_type _Metadelim = _Traits::to_int_type(postfix[match_count]);
			typename _Traits::int_type _Meta = istr.rdbuf()->sgetc();

			for (; ; _Meta = istr.rdbuf()->snextc())
			{
				if (_Traits::eq_int_type(_Traits::eof(), _Meta))
				{	// end of file, quit
					_State |= ios_base::eofbit;
					break;
				}
				else if (_Traits::eq_int_type(_Meta, _Metadelim))
				{
					if(++match_count == postfix.length())
					{
						// got all delimiter, discard it and quit
						_Changed = true;
						istr.rdbuf()->sbumpc();
						break;
					}
					_Metadelim = _Traits::to_int_type(postfix[match_count]);
				}
				else
				{	// got a character
					if(match_count > 0)
					{
						line += postfix.substr(0, match_count);
						match_count = 0;
						_Metadelim = _Traits::to_int_type(postfix[match_count]);
					}
					// add it to string
					line += _Traits::to_char_type(_Meta);
					_Changed = true;
				}
			}
		}
        catch(...)
		{
			istr.setstate(ios_base::badbit);
		}
	}

	if (!_Changed)
		_State |= ios_base::failbit;
	istr.setstate(_State);
	return (istr);
}

template<class _Elem,class _Traits,class _Alloc> 
inline	basic_istream<_Elem, _Traits>& 
	getline_crlf(basic_istream<_Elem, _Traits>& istr,	basic_string<_Elem, _Traits, _Alloc>& line)
{
	static const string postfix = "\r\n";
	return getline(istr, line, postfix);
}
template<class _Elem,class _Traits> 
inline basic_ostream<_Elem, _Traits>& crlf(basic_ostream<_Elem, _Traits>& ostr)
{
	ostr << "\r\n";
	return ostr;
}
inline string& escape_backspace(string& text)
{
	string::size_type read_idx = 0, write_idx = 0;
	for(; read_idx < text.size(); ++read_idx)
	{
		if(text[read_idx] == '\b')
		{
			if(write_idx > 0)
			{
				--write_idx;
			}
		}
		else
		{
			text[write_idx] = text[read_idx];
			++write_idx;
		}
	}
	text.resize(write_idx);
	return text;
}
inline string escape_backspace_const(const string& text)
{
	string temp = text;
	return escape_backspace(temp);
}
class FormatOutputAlign
{
public:
	static const int Near	= 0;
	static const int Center = 1;
	static const int Far	= 2;
};

template <typename T>
inline string FormatOutput(const T& data, int width, int align = FormatOutputAlign::Near)
{
	ostringstream ostr;
	ostr << data;
	string strOutput = ostr.str();
	if((int)strOutput.length() >= width)
		return strOutput;
	switch(align)
	{
		case FormatOutputAlign::Near:
			return strOutput + string(width - strOutput.length(), ' ');
		case FormatOutputAlign::Center:
			{
				int space = width - strOutput.length();
				int front = space / 2;
				int back = space - front;
				return string(front, ' ') + strOutput + string(back, ' ');
			}
		case FormatOutputAlign::Far:
			return string(width - strOutput.length(), ' ') + strOutput;
		default:
			return strOutput;
	}
}

extern string EmptyString;

#ifdef WIN32

inline string GetErrorMessage(u_int32_t error_num)
{
	char * lpMsgBuf = NULL;
	if(FormatMessage( 
		FORMAT_MESSAGE_ALLOCATE_BUFFER | 
		FORMAT_MESSAGE_FROM_SYSTEM | 
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		error_num,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
		(LPTSTR) &lpMsgBuf,
		0,
		NULL ) > 0)
	{
		string ret = lpMsgBuf;
		LocalFree(lpMsgBuf);
		return ret;
	}
	else
	{
		ostringstream ostr;
		ostr << "FormatMessage failed when retrieving the error message of :" << error_num << ".";
		return ostr.str();
	}
}
#endif


// macros used to convert between ascii code and utf-8 code

#ifdef WIN32

#define MT_USE_CONVERSION int __w_buf_size__, __m_buf_size__;wchar_t* __w_buf__;char* __m_buf__;

#define A2U_len(str, len) \
	(((str) == NULL) ? (__m_buf_size__ = 0, "") : (__w_buf_size__ = (int)(len) + 1, __w_buf__ = (wchar_t*)alloca(__w_buf_size__ << 2), MultiByteToWideChar(CP_THREAD_ACP, 0, (char*)(str), -1, __w_buf__ , (int)__w_buf_size__), \
	__m_buf_size__ = WideCharToMultiByte(CP_UTF8, 0, __w_buf__, -1, NULL, 0, NULL, NULL), __m_buf__ = (char*)alloca(__m_buf_size__), \
	WideCharToMultiByte(CP_UTF8, 0, __w_buf__, -1, __m_buf__, __m_buf_size__, NULL, NULL), __m_buf__))

#define U2A_len(str, len)\
	(((str) == NULL) ? (__m_buf_size__ = 0, "") : (__w_buf_size__ = (int)(len) + 1, __w_buf__ = (wchar_t*)alloca(__w_buf_size__ << 2), MultiByteToWideChar(CP_UTF8, 0, (char*)(str), -1, __w_buf__ , (int)__w_buf_size__), \
	__m_buf_size__ = WideCharToMultiByte(CP_THREAD_ACP, 0, __w_buf__, -1, NULL, 0, NULL, NULL), __m_buf__ = (char*)alloca(__m_buf_size__), \
	WideCharToMultiByte(CP_THREAD_ACP, 0, __w_buf__, -1, __m_buf__, __m_buf_size__, NULL, NULL), __m_buf__))

#define MT_CONV_LEN		__m_buf_size__

#else

#define MT_USE_CONVERSION
#define A2U_len(str, len) ((str) == NULL ? "" : (const char*)(str))
#define U2A_len(str, len) ((str) == NULL ? "" : (const char*)(str))

#endif


#define A2U(str) A2U_len((str), strlen((const char*)(str)))
#define U2A(str) U2A_len((str), strlen((const char*)(str)))

#endif // !defined(STRING_UTIL_H)

